package com.atguigu.springcloud.extend.impl;

import com.atguigu.springcloud.extend.RedisLock;
import com.atguigu.springcloud.extend.RedisService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;

import java.util.UUID;

/**
 * @author xiaolong
 * @version 1.0
 * @description: redis分布式锁
 * @date 2021/10/19 17:22
 */
@ConditionalOnProperty(name = "gosuncn.lock.local", havingValue = "redis", matchIfMissing = false)
@Slf4j
public class RedisDistributedLock implements RedisLock {

    @Autowired
    private RedisService redisService;


    private final ThreadLocal<String> unLockId = new ThreadLocal<String>();

    /**
     * @param key          键
     * @param unLock       解锁需要用到uuid
     * @param maxWaitTime  最大的等待时间
     * @param timeInterval 间隔时间
     * @return true  成功， false 失败
     * @description: 创建锁
     */
    private boolean getLock(String key, String unLock, long maxWaitTime, long timeInterval) {
        long temp = timeInterval;
        //间隔时间<最大的等待时间
        while (temp < maxWaitTime) {
            try {
                if (redisService.getLock(key, unLock, 60)) {
                    log.info("get key:{}", key);
                    return true;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

            try {
                //沉睡等待时间后再继续请求获得锁，等到当前的锁过期
                Thread.sleep(timeInterval);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //当达到最大的等待时间不再进行请求锁
            temp += timeInterval;
        }
        return false;
//        return redisService.getLock(key,unLock,60);
    }

    /**
     * @param key          键
     * @param device       没用到
     * @param maxWaitTime  最大的等待时间
     * @param timeInterval 间隔时间
     * @return true  成功， false 失败
     * @description: 加锁
     */
    @Override
    public boolean tryLock(String key, String device, long maxWaitTime, long timeInterval) {
        String unlock = UUID.randomUUID().toString();
        //存入当前线程对象中ThreadLocal
        unLockId.set(unlock);
        boolean flag = false;
        try {
            flag = getLock(key, unlock, maxWaitTime, timeInterval);
            System.out.println("flag" + flag);
        } finally {
            if (!flag) {
                unLockId.remove();
            }
        }
        return flag;
    }

    /**
     * @param keys         键集合
     * @param maxWaitTime  最大的等待时间
     * @param timeInterval 间隔时间
     * @return
     * @description: 批量加锁，共用一个uuid，
     */
    @Override
    public boolean tryLock(String[] keys, String device, long maxWaitTime, long timeInterval) {
        String unlock = UUID.randomUUID().toString();
        unLockId.set(unlock);
        boolean flag = false;
        try {
            int i = 1;
            for (String key : keys) {
                if (getLock(key, unlock, maxWaitTime / (i++), timeInterval)) {
                    continue;
                }
                flag = false;
                return false;
            }
        } finally {
            if (!flag) {
                unLockId.remove();
            }
        }
        return true;
    }

    /**
     * @param key
     * @return
     * @description: 解锁
     */
    @Override
    public void unLock(String key, String device) {
        //获取锁的uuid
        String unLock = unLockId.get();
        if (unLock != null && !"".equals(unLock)) {
            try {
                redisService.unLock(key, unLock);
                log.info("解锁成功");
            } finally {
                unLockId.remove();
            }
        }
    }

    /**
     * @param keys key的集合
     * @return
     * @description: 批量解锁
     */
    @Override
    public void unLock(String[] keys, String device) {
        //从后到前
        String unLock = unLockId.get();
        if (unLock == null || "".equals(unLock)) {
            return;
        }
        try {
            for (int i = keys.length; i >= 0; i--) {
                redisService.unLock(keys[i], unLock);
                log.info("remove key:{}", keys[i]);
            }
        } finally {
            unLockId.remove();
        }

    }


}
